#pragma once
// socket基类

#include <vector>
#include <list>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <pthread.h>
#include <semaphore.h>
#include <atomic>
#include <map>

#include "comm.h"

// 一些socket相关的专用宏定义和结构体定义
#define LISTEN_BACKLOG  511// 全连接队列
#define MAX_EVENTS  512// epoll_wait一次接收最多事件个数(单次轮询的最大事件数)

// 前置声明避免循环依赖
typedef struct listening_s   listening_t, *lplistening_t;
typedef struct connection_s  connection_t,*lpconnection_t;
typedef class  CSocket  CSocket;
typedef void (CSocket::*event_handler_pt)(lpconnection_t c); //定义类的成员函数指针类型，类型名为event_handler_pt
// void (...)(lpconnection_t c),所以上述代码实际上定义了一个指向CSocket类成员函数指针


// 和监听端口有关的结构体
struct listening_s  
{
	int port;        // 监听的端口号
	int fd;          // 套接字句柄socket
	lpconnection_t connection;  // TCP连接池指针保存对应的一个TCP连接 
};

// TCP连接结构体（客户端主动发起的、服务器被动接受的TCP连接）
struct connection_s
{
    // TCP套接字信息相关
    int fd;// 套接字句柄
    lplistening_t listening;// 每个TCP连接对应的监听端口结构体
    uint64_t iCurrsequence;// 自增字段，记录连接序号
    struct sockaddr s_sockaddr;// 保存对方地址信息

    // 读写操作相关
    event_handler_pt rhandler;// CSocket类的函数指针类型，保存处理读事件的成员函数
    event_handler_pt whandler;// CSocket类的函数指针类型，保存处理写事件的成员函数

    // epoll相关
    uint32_t events;// 记录该连接关注的事件类型(如EPOLLIN、EPOLLOUT等)
    
    // 和收包相关
    unsigned char curStat;// 当前收包的状态(8位无符号整形0-255)
    char dataHeadInfo[_DATA_BUFSIZE_];// 保存收到的数据的包头信息（包头固定大小为20字节）
    char *precvbuf;// 接收缓冲区的头指针(对收到不全的包非常有用)
    unsigned int irecvlen;// 指定收的数据长度
    char *precvMemPointer;// 保存用于收包的内存的首地址，释放时使用

    // 处理内部逻辑的互斥锁，保证内部处理的线程安全
    pthread_mutex_t logicPorcMutex;

    // 和发包相关
    std::atomic<int> iThrowsendCount;// 发送缓冲区标志(0表示空闲)（如果发送缓冲区满，需要通过epoll事件来驱动消息的继续发送）
    char *psendMemPointer;// 整个数据包的头指针（消息头+包头+包体）,释放时使用
    char *psendbuf;// 发送缓冲区头指针（包头+包体）
    unsigned int isendlen;// 指定发的数据长度

    // 和回收相关
    time_t inRecyTime;// 保存放置到回收队列中的时间（用于判断是否真正需要回收）

    // 和心跳包相关
    time_t lastPingTime;// 上次发送心跳包的时间

    // 和网络安全相关（仅预防Flood攻击）
    uint64_t FloodkickLastTime;// Flood攻击上次收到包的时间
    int FloodAttackCount;// Flood攻击在该时间内收到包的次数统计
    std::atomic<int> iSendCount;// 发送队列中有的数据条目数，若client只发不收，则可能造成此数过大，依据此数做出踢出处理 

    lpconnection_t next;// 指向下一个TCP连接对象，用于串联成链表方便取用

    // 结构体函数
    connection_s();// 构造函数
    virtual ~connection_s();// 虚析构函数
    void GetOneToUse();// 尝试分配一个TCP连接前的自初始化函数
    void PutOneToFree();// 尝试回收一个TCP连接前的自初始化函数
};

// 消息头额外信息结构体(并不是数据包的内部字段头，而是额外记录一些内容备用的结构体)
typedef struct _STRUC_MSG_HEADER{
    lpconnection_t pConn;// 记录对应的连接
    uint64_t iCurrsequence;// 收到数据包时记录对应连接的序号，将来能用于比较是否连接已经作废用
}STRUC_MSG_HEADER,*LPSTRUC_MSG_HEADER;

// socket类(基类)
class CSocket{
public:
    CSocket();
    virtual ~CSocket();
    virtual bool Initialize();// 初始化函数
    virtual bool Initialize_subproc();// 互斥量和线程初始化函数(初始化互斥量和线程)
    virtual void Shutdown_subproc();// 关闭退出函数

    void PrintTDInfo();// 打印统计信息

    // 处理客户端请求，虚函数，因为有子类继承本类
    virtual void ThreadRecvProcFunc(char *pMsgBuf);
    //心跳包检测时间到，该去检测心跳包是否超时的事宜，本函数只是把内存释放，子类应该重新实现该函数以实现具体的判断动作
    virtual void ProcPingTimeOutChecking(LPSTRUC_MSG_HEADER tmpmsg,time_t cur_time);

    // epoll相关
    int Epoll_init();// epoll功能初始化
    int Epoll_process_events(int timer);// epoll等待接收和处理事件
    int Epoll_oper_event(int fd,uint32_t eventtype,uint32_t flag,int bcaction,lpconnection_t pConn);// epoll操作事件

    // 连接池相关
    lpconnection_t Get_connectionByFd(int fd);// 根据套接字获取到对应的客户端连接

protected:
    // 数据发送相关
    void MsgSend(char *psendbuf);// 把数据扔到待发送对列中
    void ZdClosesocketProc(lpconnection_t p_Conn);// 主动关闭一个连接时的要做些善后的处理函数

    // 一些和网络通讯有关的成员变量（供后续频繁使用时提高效率）
    size_t m_iLenPkgHeader;// 包头长度
    size_t m_iLenMsgHeader;// 消息头长度

    // 时间相关
    int m_ifTimeOutKick;// 当时间到达Sock_MaxWaitTime指定的时间时，直接把客户端踢出去，只有当Sock_WaitTimeEnable = 1时，本项才有用
    int m_iWaitTime;// 多少秒检测一次是否心跳超时，只有当Sock_WaitTimeEnable = 1时，本项才有用

private:
    // 套接字版线程池对象结构体
    struct ThreadItem{
        pthread_t   _Handle;// 线程句柄
        CSocket     *_pThis;// 记录套接字版线程池的指针，用来指向自己
        bool        ifrunning;// 标记是否正式启动起来，启动起来后，才允许调用StopAll()来释放

        ThreadItem(CSocket *pthis):_pThis(pthis),ifrunning(false){}
        ~ThreadItem(){} 
    };

    // epoll套接字相关
    int m_worker_connections; // epoll连接的最大项数
	int m_ListenPortCount;// 所监听的端口数量
	int m_epollhandle;// epoll_create返回的句柄
    std::vector<lplistening_t> m_ListenSocketList;// 监听套接字队列
    struct epoll_event m_events[MAX_EVENTS];// 用于在epoll_wait()中承载返回的所发生的事件

    // 和连接池相关
    std::list<lpconnection_t> m_connectionList;// 连接列表【连接池】
    std::list<lpconnection_t> m_freeconnectionList;// 空闲连接列表【这里边装的全是空闲的连接】
    std::list<lpconnection_t> m_recyconnectionList;// 将要释放的连接放这里
    std::atomic<int> m_total_connection_n;// 连接池总连接数
    std::atomic<int> m_free_connection_n;// 连接池空闲连接数
    std::atomic<int> m_totol_recyconnection_n;// 待释放连接队列大小
    pthread_mutex_t m_connectionMutex;// 连接池相关互斥量，互斥连接列表和空闲列表
    pthread_mutex_t m_recyconnqueueMutex;// 连接回收队列相关的互斥量
    int m_RecyConnectionWaitTime;//等待多少秒后才回收连接

    // 消息队列相关
    std::list<char *> m_MsgSendQueue;// 发送数据消息队列
    std::atomic<int> m_iSendMsgQueueCount;// 发消息队列大小

    // 多线程相关
    std::vector<ThreadItem *> m_threadVector;// 保存线程的容器 
    pthread_mutex_t m_sendMessageQueueMutex;// 发消息队列互斥量
    sem_t m_semEventSendQueue;// 处理发消息线程相关的信号量

    // 时间相关
    int  m_ifkickTimeCount;// 是否开启踢人时钟，1：开启   0：不开启
    pthread_mutex_t m_timequeueMutex;// 和时间队列有关的互斥量
    std::multimap<time_t, LPSTRUC_MSG_HEADER> m_timerQueuemap;// 存储按时间排序的套接字映射（队列）
    size_t m_cur_size_;// 时间队列的当前长度
    time_t m_timer_value_;// 当前队列头部的时间的值

    // 在线用户数量
    std::atomic<int> m_onlineUserCount;// 当前在线用户数统计

    // 网络安全相关
    int m_floodAkEnable;// Flood攻击检测是否开启,1：开启   0：不开启
    unsigned int m_floodTimeInterval;// 表示每次收到数据包的时间间隔是100(毫秒)
    int m_floodKickCount;// 累积多少次踢出此人

    // 统计用途
    time_t m_lastprintTime;// 上次打印统计信息的时间(10秒钟打印一次)
    int m_iDiscardSendPkgCount;// 丢弃的发送数据包数量

    void ReadConf();// 专门用于读各种配置项
    bool Open_listening_sockets();// 监听必须的端口【支持多个端口】
    void Close_listening_sockets();// 关闭监听套接字
    bool Setnonblocking(int sockfd);// 设置非阻塞套接字

    // 一些业务处理函数handler
    void Event_accept(lpconnection_t oldc);// 建立新连接
    void Read_request_handler(lpconnection_t pConn);// 设置数据来时的读处理函数
    void Write_request_handler(lpconnection_t pConn);// 设置数据发送时的写处理函数
    void Close_connection(lpconnection_t pConn);// 通用连接关闭函数,资源用这个函数释放
    ssize_t Recvproc(lpconnection_t pConn,char *buff,ssize_t buflen);// 接收从客户端来的数据专用函数
    void Wait_request_handler_proc_p1(lpconnection_t pConn,bool &isflood);// 包头收完整后的处理，我们称为包处理阶段1：写成函数，方便复用
    void Wait_request_handler_proc_plast(lpconnection_t pConn,bool &isflood);// 收到一个完整包后的处理，放到一个函数中，方便调用
    void ClearMsgSendQueue();// 清理发送消息队列
    ssize_t Sendproc(lpconnection_t c,char *buff,ssize_t size);// 将数据发送到客户端

    // 获取对端信息相关
    size_t Sock_ntop(struct sockaddr *sa,int port,u_char *text,size_t len);// 获取地址端口字符串，返回这个字符串的长度

    // 连接池或连接相关
    void Initconnection();// 初始化连接池
    void Clearconnection(); // 回收连接池
    lpconnection_t Get_connection(int fd);// 从连接池中获取一个空闲连接
    void Free_connection(lpconnection_t pConn);// 归还参数pConn所代表的连接到到连接池中
    void InRecyConnectQueue(lpconnection_t pConn);// 将要回收的连接放到一个队列中来

    // 和时间相关的函数（心跳超时）
    void AddToTimerQueue(lpconnection_t pConn);// 设置踢出时钟(向map表中增加内容)
    time_t GetEarliestTime();// 从multimap中取得最早的时间返回去
    // 从m_timeQueuemap移除最早的时间，并把最早这个时间所在的项的值所对应的指针返回，由调用者负责互斥
    LPSTRUC_MSG_HEADER RemoveFirstTimer();
    // 根据给的当前时间，从m_timeQueuemap找到比这个时间更老（更早）的节点【1个】返回去，这些节点都是时间超过了，要处理的节点
    LPSTRUC_MSG_HEADER GetOverTimeTimer(time_t cur_time);
    void DeleteFromTimerQueue(lpconnection_t pConn);// 把指定用户tcp连接从timer表中抠出去
    void ClearAllFromTimerQueue();// 清理时间队列中所有内容

    // 和网络安全相关
    bool TestFlood(lpconnection_t pConn);// 测试是否flood攻击成立，成立则返回true，否则返回false

    // 线程相关函数
    static void* ServerSendQueueThread(void *threadData);// 专门用来发送数据的线程
    static void* ServerRecyConnectionThread(void *threadData);// 专门用来回收连接的线程
    static void* ServerTimerQueueMonitorThread(void *threadData);// 时间队列监视线程，处理到期不发心跳包的用户踢出的线程
};